package com.societyGames.flashForms
{
import flash.display.DisplayObject;
import flash.display.Stage;
import flash.events.MouseEvent;
import flash.ui.Mouse;

public class MouseCursor
{
  private static var _instance:MouseCursor;
  private var _setObject:Object;
  private var _lockObject:Object;
  private var _cursor:DisplayObject;
  private var _stage:Stage;
  private var _isCursorVisible:Boolean = true;
  private var _dx:Number;
  private var _dy:Number;

  public static function initialize(stage:Stage):void
  {
    if (_instance != null)
    {
      throw new Error("Cannot reinitialize singleton.");
    }
    _instance = new MouseCursor(stage);
  }

  public static function get instance():MouseCursor
  {
    return _instance;
  }

  public function MouseCursor(stage:Stage)
  {
    if (stage == null)
    {
      throw new ArgumentError("Argument cannot be null.");
    }
    this._stage = stage;
    this._stage.addEventListener(MouseEvent.ROLL_OUT, rollOutHandler);
    this._stage.addEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
  }

  /**
   * Sets the cursor as long as it is not locked.
   * @param cursor The cursor to use.
   * @param setObject The object to check for on a unconditional unset.
   */
  public function setCursor(cursor:DisplayObject, setObject:Object = null, dx:Number = 0, dy:Number = 0):void
  {
    if (this._lockObject == null)
    {
      clearCursor();
      this._setObject = setObject;
      useCursor(cursor, dx, dy);
      Mouse.hide();
    }
  }

  /**
   * Unsets the cursor.
   * @param setObject If null, will unset any cursor. If non-null will only unset the cursor if the setObject matches.
   */
  public function unsetCursor(setObject:Object = null):void
  {
    if (this._lockObject == null)
    {
      if (setObject == null || setObject == this._setObject)
      {
        clearCursor();
        this._setObject = null;
      }
    }
  }

  /**
   * Sets the cursor, only to be unlocked by a particular object.
   * @param cursor The cursor to use.
   * @param lockObject The object that can unlock the cursor.
   **/
  public function lockCursor(cursor:DisplayObject, lockObject:Object, dx:Number = 0, dy:Number = 0):void
  {
    if (this._lockObject == null || this._lockObject == lockObject)
    {
      clearCursor();
      this._setObject = null;
      this._lockObject = lockObject;
      useCursor(cursor, dx, dy);
    }
  }

  public function unlockCursor(lockObject:Object):void
  {
    if (this._lockObject == lockObject)
    {
      clearCursor();
      this._lockObject = null;
      Mouse.show();
    }
  }

  private function rollOverHandler(event:MouseEvent):void
  {
    this._isCursorVisible = true;
    this.synchronizeCursorVisibility();
  }

  private function rollOutHandler(event:MouseEvent):void
  {
    this._isCursorVisible = false;
    this.synchronizeCursorVisibility();
  }

  private function mouseMoveHandler(event:MouseEvent):void
  {
    this._cursor.x = int(this._stage.mouseX) + _dx;
    this._cursor.y = int(this._stage.mouseY) + _dy;
  }

  private function synchronizeCursorVisibility():void
  {
    if (this._cursor != null)
    {
      this._cursor.visible = this._isCursorVisible;
    }
  }

  private function useCursor(cursor:DisplayObject, dx:Number, dy:Number):void
  {
    this._cursor = cursor;
    this._dx = dx;
    this._dy = dy;

    if (this._cursor != null)
    {
      this._stage.addChild(this._cursor);
      this._cursor.x = int(this._stage.mouseX) + this._dx;
      this._cursor.y = int(this._stage.mouseY) + this._dy;
      synchronizeCursorVisibility();
      this._stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
    }
    Mouse.hide();
  }

  private function clearCursor():void
  {
    if (this._cursor != null)
    {
      this._stage.removeChild(this._cursor);
      this._stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
      this._cursor = null;
    }
    Mouse.show();
  }
}
}